#!/usr/bin/env python
# coding=utf-8

# Pyddns version 0.2.0 for DNSPod API 2.8
# Released under GPLv3.
# Author : ichinihachi@gmail.com 2011-2

import urllib
import urllib2
import os
import socket
import json
import re
import sys
import pyDes
import base64

# config file
conf_dir = os.path.dirname(__file__)
conf_file = os.path.join(conf_dir, 'Pyddns.conf')

# global variables
user = ''
password = ''
key = 'abcdefghijklmnop' # u can change this key(must be 16 bytes)
three_des = pyDes.triple_des(key, mode=pyDes.CBC, IV="\0\1\2\3\4\5\6\7", pad=None, padmode=pyDes.PAD_PKCS5)
record_info = dict()
##user_id = ''
##domain_id = ''
##record_id =''
ip =''

# Pyddns's user agent
user_agent = 'Pyddns/0.2.0(ichinihachi@gmail.com)'
headers = { 'User-Agent' : user_agent }

# request urls
api_ver_url = 'https://dnsapi.cn/Info.Version'
user_info_url = 'https://dnsapi.cn/User.Info'
domain_list_url = 'https://dnsapi.cn/Domain.List'
domain_info_url = 'https://dnsapi.cn/Domain.Info'
record_type_url = 'https://dnsapi.cn/Record.Type'  #获取成功后要在本地保存一份，不要操作一次获取一次
record_line_url = 'https://dnsapi.cn/Record.Line'  #获取成功后要在本地保存一份，不要操作一次获取一次
record_list_url = 'https://dnsapi.cn/Record.List'
record_mod_url = 'https://dnsapi.cn/Record.Modify'
record_cre_url = 'https://dnsapi.cn/Record.Create'

def encryptStr(data):
    en = three_des.encrypt(data)
    return base64.b64encode(en)

def decryptStr(data):
    de = base64.b64decode(data) 
    return three_des.decrypt(de)

def readConf(fileopen):
    global user, password, ip, record_info
    names = list()
    values = list()
    sub_domains = dict()
    # read config file
    try:
        fp = open(fileopen, 'r')
    except IOError:
        # fail to read config file
        print 'Error : Cannt open the config file!'
        exit()
    # get your config data
    while 1:
        line = fp.readline()
        if not line:
            break
        line = line.strip()
        if (line == '')|(line.startswith('#')):
            # empty line or comments
            continue
        (name, sep, value) = line.partition('=')
        if sep == '=':
            name = name.strip().lower()
            value = value.strip()
            if name == 'user':
                user = decryptStr(value)
            elif name == 'password':
                password = decryptStr(value)
            else :
                names.append(name)
                values.append(value)
    fp.close()
    # request for username and password if not found in config file
    if user == '' and password == '':
        user = raw_input('Enter your username: ')
        password = raw_input('Enter your password: ')
        conf_file = open(fileopen).read()
        conf_file = 'user = '+ encryptStr(user) +'\n' +\
                    'password =' + encryptStr(password) +'\n' +\
                    conf_file
        # if u dont want to save account info in config file , comment the next 3 lines with "#"
        fp = open(fileopen, 'w')
        fp.write(conf_file)
        fp.close()
    while names:
        # get domain_name
        if names[0] == 'domain_name':
            names.pop(0)
            domain_name = [values.pop(0)]
        else :
            print 'Error : check for your config'
            exit()
        # get sub_domain
        while names[0] == 'sub_domain':
            names.pop(0)
            sub_domain = [values.pop(0)]
            # get record_value
            if not names:
                sub_domains.update(dict.fromkeys(sub_domain,ip)) 
                break
            elif names[0] == 'record_value':
                names.pop(0)
                sub_domains.update(dict.fromkeys(sub_domain,values.pop(0)))
                if not names:
                    break
            elif names[0] != 'record_value':
                sub_domains.update(dict.fromkeys(sub_domain,ip))            
        record_info.update(dict.fromkeys(domain_name,sub_domains))
        sub_domains = dict()
    return

def getIp():
    global ip
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(("dnsapi.cn",443))
    except socket.gaierror:
        print 'Error : Fail to connect DNSPod(dnsapi.cn)!'
        exit()
    ip = s.getsockname()[0]
    ips = ip.split(".")
    s.close()
    # If this ip belongs to Private IPv4 address spaces, use another method to get ip
    # class A : 10.0.0.0 – 10.255.255.255
    # class Bs : 172.16.0.0 – 172.31.255.255
    # class Cs : 192.168.0.0 – 192.168.255.255
    if (ips[0] == '10')or((ips[0] == '172')and((ips[1] >= '16')or(ips[1] < '32')))or((ips[0] == '192')and(ips[1] =='168')):
        try:
            ip = re.search('\d+\.\d+\.\d+\.\d+',urllib2.urlopen("http://www.ip138.com/ip2city.asp").read()).group(0)
        except:
            try:
                ip = re.search('\d+\.\d+\.\d+\.\d+',urllib2.urlopen("http://www.whereismyip.com/").read()).group(0)
            except:
                 print 'Error : Fail to get your IP!'
                 sys.exit()
    return ip

def getAPIVer():
    data = urllib.urlencode(values)
    ver_req = urllib2.Request(api_ver_url, data, headers)
    response = urllib2.urlopen(ver_req)
    js = response.read()
    return json.loads(js)

def getUserInfo():
    data = urllib.urlencode(values)
    user_info_req = urllib2.Request(user_info_url, data, headers)
    response = urllib2.urlopen(user_info_req)
    js = response.read()
    return json.loads(js)

def getDomainList():
    temp_values = values.copy()
    temp_values['type'] = 'mine'
    temp_values['offset'] = '0'
    temp_values['length'] = '20'
    data = urllib.urlencode(temp_values)
    domain_list_req = urllib2.Request(domain_list_url, data, headers)
    response = urllib2.urlopen(domain_list_req)
    js = response.read()
    return json.loads(js)

def getRecordList(domain_id):
    temp_values = values.copy()
    temp_values['domain_id'] = domain_id
    temp_values['offset'] = '0'
    temp_values['length'] = '20'
    data = urllib.urlencode(temp_values)
    record_list_req = urllib2.Request(record_list_url, data, headers)
    response = urllib2.urlopen(record_list_req)
    js = response.read()
    return json.loads(js)

def recordModify(sub_domain, ip, domain_id, record_id):
    temp_values = values.copy()
    temp_values['domain_id'] = domain_id
    temp_values['record_id'] = record_id
    temp_values['sub_domain'] = sub_domain
    temp_values['record_type'] = 'A'
    temp_values['record_line'] = '默认'
    temp_values['value'] = ip
    temp_values['mx'] = '0'
    temp_values['ttl'] = '600'  #范围1-604800，不同等级域名最小值不同，免费域名最低600
    data = urllib.urlencode(temp_values)
    record_mod_req = urllib2.Request(record_mod_url, data, headers)
    response = urllib2.urlopen(record_mod_req)
    js = response.read()
    return json.loads(js)

def recordCreate(sub_domain, ip, domain_id):
    temp_values = values.copy()
    temp_values['domain_id'] = domain_id
    temp_values['sub_domain'] = sub_domain
    temp_values['record_type'] = 'A'
    temp_values['record_line'] = '默认'
    temp_values['value'] = ip
    temp_values['mx'] = '0'
    temp_values['ttl'] = '600'  #范围1-604800，不同等级域名最小值不同，免费域名最低600
    data = urllib.urlencode(temp_values)
    record_cre_req = urllib2.Request(record_cre_url, data, headers)
    response = urllib2.urlopen(record_cre_req)
    js = response.read()
    return json.loads(js)

def domainUpdate(domain_name, sub_domain_dict, domain_id):
    record_list = getRecordList(domain_id)
    if record_list.get('status').get('code') == '1':
        print 'Getting record list ...'
    else:
        print 'Error : Fail to get \"' + domain_name + '\" \'s record list!'
        print 'Error Code : ' + record_list.get('status').get('code')
        print 'Error Message : '+ record_list.get('status').get('message')
        return
    # set x as sub_domain names
    for x in sub_domain_dict.keys():
        # get record's id
        for y in record_list.get('records'):
            if y.get('name') == x:
                record_id = y.get('id')
                print 'OK. Sub domain \"' + x + '\" \'s ' + 'record id is ' + str(record_id) + '.'
                break
        else:
            record_id = ''
            print 'Sub domain \"' + x + '\" not found. A new record will be created.'
        # update the old record or create a new record
        if record_id:
            record_mod = recordModify(x, sub_domain_dict.get(x), domain_id, record_id)
            if record_mod.get('status').get('code') == '1':
                print 'Record modified success!'
            else:
                print 'Error : Fail to modify the record!'
                print 'Error Code : ' + record_mod.get('status').get('code')
                print 'Error Message : '+ record_mod.get('status').get('message')
                return
        else:
            record_cre = recordCreate(x, sub_domain_dict.get(x), domain_id)
            if record_cre.get('status').get('code') == '1':
                print 'Record created success!'
            else:
                print 'Error : Fail to create the record!'
                print 'Error Code : ' + record_cre.get('status').get('code')
                print 'Error Message : '+ record_cre.get('status').get('message')
                return
    return

def ddnsUpdate():
    #global user_id, domain_id, record_id
    api_ver = getAPIVer()
    if api_ver.get('status').get('code') == '1':
        print 'Getting API version : ' + api_ver.get('status').get('message')
    else:
        print 'Error : Fail to get API version!'
        print 'Error Code : ' + api_ver.get('status').get('code')
        print 'Error Message : '+ api_ver.get('status').get('message')
        print ''
    
    user_info = getUserInfo()
    if user_info.get('status').get('code') == '1':
        print 'Getting user info : ' + user_info.get('user').get('email')
    else:
        print 'Error : Fail to get user info!'
        print 'Error Code : ' + user_info.get('status').get('code')
        print 'Error Message : '+ user_info.get('status').get('message')
        print ''

    domain_list = getDomainList()
    if domain_list.get('status').get('code') == '1':
        print 'Getting domain list ...'
    else:
        print 'Error : Fail to get domain list!'
        print 'Error Code : ' + domain_list.get('status').get('code')
        print 'Error Message : '+ domain_list.get('status').get('message')
        return
    # set x as domain names
    for x in record_info.keys():
        # get domain's id
        for y in domain_list.get('domains'):
            if y.get('name') == x:
                domain_id = y.get('id')
                # update each domain name
                print 'OK. Domain name \"' + x + '\" \'s ' + 'id is ' + str(domain_id) + '.'
                domainUpdate(x, record_info.get(x), domain_id)
                break
        else:
            print ''
            print 'Error : Domain name \"' + x + '\" not found! Please check for your config file.'
            print ''
    return


if __name__ == "__main__":
    ip = getIp()
    readConf(conf_file)
    
    print '=========================================='
    print 'user:%s' %user
    for x in record_info.keys():
        print 'domain_name:%s' %x
        for y in record_info.get(x).keys():
            print 'sub_domain:%s (%s)' %(y,record_info.get(x).get(y))
    print '=========================================='
    
    # reqest values
    values = {'login_email' : user,
              'login_password' : password,
              'format' : 'json' }

    # post request
    ddnsUpdate()

    # If u want to keep cmd window alive,just modify "while 0" to "while 1".
    while 0:
        pass
